Zig Note

Meow King January 21, 2024 Updated: February 07, 2024 #zig #note

Array and Slice

const V = @Vector(4, i32);
const value = @as(V, @splat(0)); // [0, 0, 0, 0]
  1. make sure slice not be optimized to an array pointer
var array = [_]i32{1, 2, 3, 4};
// if we use `const`, then the variable `slice` may be optimized to a array pointer
var len: usize = 3;
var slice = array[0..len];
std.debug.print("{}", .{@TypeOf(slice)});

Struct

  1. return a struct type from a function
const std = @import("std");

fn List(comptime T: type) type { 
    return struct { 
        const Self = @This(); 

        items: []T, 

        fn length(self: Self) usize { 
            return self.items.len; 
        } 
    }; 
} 

pub fn main() !void {
    const int_list = List(u8);
    var arr: [5]u8 = .{
        1, 2, 3, 4, 5,
    };

    var list: int_list = .{
        .items = &arr,
    };

    std.debug.print("list len is {}\n", .{list.length()});
}

Enum & Union

  1. to simulate rust enum can hold any type, see 标记联合

Pointer

+---------------+----------------------------------------------+
|  u8           |  one u8                                      |
|  *u8          |  pointer to one u8                           |
|  [2]u8        |  two u8s                                     |
|  [*]u8        |  pointer to unknown number of u8s            |
|  [*]const u8  |  pointer to unknown number of immutable u8s  |
|  *[2]u8       |  pointer to an array of 2 u8s                |
|  *const [2]u8 |  pointer to an immutable array of 2 u8s      |
|  []u8         |  slice of u8s                                |
|  []const u8   |  slice of immutable u8s                      |
+---------------+----------------------------------------------+

Comptime

  1. comptime return type
fn makeLlamas(comptime count: usize) [count]u8 {}
  1. inline in switch
switch (thing) {
    .a => |a| special(a),
    inline else => |t| normal(t),
}

Built Functions

@typeInfo(Circle).Struct.fields
@field(foo, "x"); // returns the value at foo.x

Type Coversation

As of version 0.12.0-dev.2313+bf7ebfa67, we cannot do the following:

// if both `f` and `u` is const and is not computed from variable(`var`)(so it's like comptime_int, ...), then it will succeed
var f: f32 = 0.5;
f += 0.1;
var u: u32 = 10;
u += 1;
const r: u32 =  1 + f * u; // expect r == 5
std.debug.print("r = {}\n", .{r});

It will produce an error incompatible types: 'f32' and 'u32', we need to do things like this:

@as(u16, @intFromFloat(@as(f16, @floatFromInt(fz)) * spacing_ratio))

String

  1. String in zig: The truth is @TypeOf("foo") == *const [3:0]u8

    const str = "El Psy Kongaroo"; // *const [15]u8
    std.debug.print("{s}\n", .{str});
    
    const str_slice: []const u8 = "El Psy Kongaroo";
    std.debug.print("{s}\n", .{str_slice});
    
    const str_slice_ptr: [*]const u8 = "El Psy Kongaroo";
    const str_slice_ptr_printable: [*:0]const u8 = @ptrCast(str_slice_ptr);
    std.debug.print("{s}", .{str_slice_ptr_printable});
    
    const greeting: [5]u8 = .{ 'h', 'e', 'l', 'l', 'o' };
    std.debug.print("{s}\n", .{greeting});
    
    // slice of string
    const slice_of_string: []const []const u8 = &.{"El", "Psy", "Kongaroo"};
    
  2. Concat String
    At Compile Time

// 1
const final_url = "https://github.com/" ++ user ++ "/reponame";
// 2
const final_url = comptime std.fmt.comptimePrint("https://github.com/{s}/reponame", .{user});

At Runtime

// 1, with allocation, use `allocPrintZ` to make sure it can interact with C freely (with `\x00` ending), see 2th below
const final_url = try std.fmt.allocPrint(alloc, "https://github.com/{s}/reponame", .{user});
defer alloc.free(final_url);
// 2, with allocation (we add `\x00` to make sure we can use it to interact with C freely. i.e. @ptrCast to `[*c]const u8`)
const s1: []const u8 = try std.mem.concatWithSentinel(allocator, u8, &.{text, "hello"}, '\x00');
// 3, with allocation
var list = ArrayList(u8).init(allocator);
defer list.deinit();
try list.append('H');
try list.append('e');
try list.append('\x00');
const s1 = list.items[0..list.items.len - 1]; // we don't need to explicitly include `\x00` in our arry, or we will print `He^@` in Zig

// 2, no allocation
var buffer = [_]u8{undefined} ** 100;
const printed = try std.fmt.bufPrint(&buffer, "https://github.com/{s}/reponame", .{user});
  1. use std.fmt.bufPrint to format string.

Design Pattern

Singleton

Global variables are singletons
see Global Variables / Singletons

Memory

fn foo() {
    var foo: [200]const u8 = undefined; // memory will be erased
    // correct: 
    // const payload: []u8 = try allocator.alloc(u8, payload_len);
    const foo1: [200]const u8 = "hi"; // comptime 
}

Miscellaneous

  1. .? == orelse unreachable
  2. Naming: const @"555a" = 1;
  3. Use anytype to receive parameter of any type. fn printCircle(circle: anytype) void {
const stru = .{1, 2, 3};
std.debug.print("{}\n", .{stru.@"0"});
std.debug.print("{} {} {}\n", stru);
  1. inline for and inline while
  2. interface in zig
const Insect = union(enum) {
    ant: Ant,
    bee: Bee,
    grasshopper: Grasshopper,

    pub fn print(self: Insect) void {
        switch (self) {
            // All of the Ant, Bee and Grasshopprt should implement `print()` method (below `case.print()`)
            inline else => |case| return case.print(),
        }
    }
};
  1. let user override default struct settings: The following code is taken from std.zig, and please see the example code of std.log also.
const root = @import("root");
const options_override = if (@hasDecl(root, "std_options")) root.std_options else struct {};
  1. When parsing JSON using functions like std.json.parseFromSlice, parseFromSliceLeaky, etc.:
const Wrapper = struct {
    ipv4: []u8,
    address: []const u8,
    endpoint: []const u8,
};

You need to defer allocator.free(Wrapper.ipv4) afterward (it will create a new memory for holding that data if not const).
But If you declare ipv4 field to be []const u8, then you don't need to manually clear that memory.

  1. use anytype as function parameter - Zig will detect whether passed type suits in comptime.
  2. pretty print structures containing string: std.log.info("response is {}", .{std.json.fmt(response)}); see https://ziggit.dev/t/how-to-print-struct-with-strings/1597/11?u=meow_king
  3. create custom formater for you struct
  4. read input: use the returned value of stdin.readUntilDelimiterOrEof as the result

Awesome Articles / Tutorials / Books

  1. Learning Zig
  2. Zig 语言圣经
  3. Zig Learning Resources
  4. zig 构建系统解析 - 第三部分: 三个部分都很棒。第三部分;讲述了添加软件包,添加库等操作。
  5. Implementing Closures and Monads in Zig
    NOTE: closure see this
  6. Code study: interface idioms/patterns in zig standard libraries
  7. Ziggit forum docs category

Example Library / Application

Application

  1. bork: A TUI chat client tailored for livecoding on Twitch.

Library

  1. cova: Commands, Options, Values, Arguments. A simple yet robust cross-platform command line argument parsing library for Zig.